﻿using nj4x;
using nj4x.Metatrader;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MQL
{
    public class OrdersHelper
    {
        public const int TRIES = 10;
        public const int WAIT_TIME = 1000;

        Account account;

        public OrdersHelper(Account account)
        {
            this.account = account;
        }

        /// <summary>
        /// Zmiana stanu transakcji na konkretną wartość
        /// </summary>
        /// <param name="TargetVolume">Ile lotsów chcemy mieć</param>
        /// <param name="VolumeDifference">Jaka minimalna zmiana na koncie</param>
        /// <param name="Symbol">Na jakim symbolu zmiana</param>
        /// <param name="MagicNumber">Magic Number</param>
        /// <param name="MinLotSize">Jaką wielkość mają Loty (nie można mniejszych niż te wartości zamówień składać)</param>
        /// <returns></returns>
        public OrderResult SendOrder(double TargetVolume, double VolumeDifference, String Symbol, int MagicNumber, double MinLotSize)
        {
            List<IOrderInfo> orders = new List<IOrderInfo>();

            double buyVolume = 0;
            double sellVolume = 0;

            foreach (var order in this.account.getOpenTrades())
            {
                if (order.GetMagic() == MagicNumber && order.GetSymbol() == Symbol)
                {
                    if (order.GetTradeOperation() == TradeOperation.OP_BUY)
                    {
                        buyVolume += order.GetLots();
                    }
                    else if (order.GetTradeOperation() == TradeOperation.OP_SELL)
                    {
                        sellVolume += order.GetLots();
                    }
                    else
                    {
                        continue;
                    }
                    orders.Add(order);
                }
            }


            double currentVolume = buyVolume - sellVolume;

            if (Math.Abs(currentVolume - TargetVolume) < VolumeDifference)
            {
                return OrderResult.TooLowDifference;
            }

            List<IOrderInfo> ordersToRemove = new List<IOrderInfo>();

            if (TargetVolume > 0 && sellVolume > 0)
            {
                // Zamknij wszystkie selle
                foreach (var order in orders)
                {
                    if (order.GetTradeOperation() == TradeOperation.OP_SELL)
                    {
                        if (!CloseOrder(order))
                        {
                            return OrderResult.Error;
                        }
                        ordersToRemove.Add(order);
                    }
                }
                currentVolume += sellVolume;
                sellVolume = 0;
            }

            if (TargetVolume < 0 && buyVolume > 0)
            {
                // Zamknij wszystkie buye
                foreach (var order in orders)
                {
                    if (order.GetTradeOperation() == TradeOperation.OP_BUY)
                    {
                        if (!CloseOrder(order))
                        {
                            return OrderResult.Error;
                        }
                        ordersToRemove.Add(order);
                    }
                }

                currentVolume -= buyVolume;
                buyVolume = 0;
            }

            // Czyścimy listę pozycji z pozycji które zostały usunięte
            foreach (var order in ordersToRemove)
            {
                orders.Remove(order);
            }
            ordersToRemove.Clear();

            double toClose;
            double toOpen;
            if (TargetVolume > 0)
            {
                // Chcemy mieć buye
                if (currentVolume > TargetVolume)
                {
                    // Zamykamy buye
                    toClose = Math.Round((currentVolume - TargetVolume)/MinLotSize) * MinLotSize;

                    foreach (var order in orders)
                    {
                        if (toClose <= 0)
                        {
                            break;
                        }


                        double lots = order.GetLots();

                        if (lots <= toClose)
                        {
                            if (!CloseOrder(order))
                            {
                                return OrderResult.Error;
                            }
                            toClose -= lots;
                        }
                        else
                        {
                            if (!CloseOrder(order, toClose))
                            {
                                return OrderResult.Error;
                            }
                            toClose = 0;
                        }
                    }

                }
                else if (currentVolume < TargetVolume)
                {
                    // Otwieramy buya
                    toOpen = Math.Round((TargetVolume - currentVolume) / MinLotSize) * MinLotSize;

                    if (OpenOrder(Symbol, TradeOperation.OP_BUY, toOpen, MagicNumber) == -1)
                    {
                        return OrderResult.Error;
                    }

                }
                else
                {
                    return OrderResult.NoChanges;
                }
            }
            else if (TargetVolume < 0)
            {
                // Chcemy mieć selle
                if (currentVolume < TargetVolume)
                {
                    // Zamykamy selle
                    toClose = Math.Round((TargetVolume - currentVolume) / MinLotSize) * MinLotSize;

                    foreach (var order in orders)
                    {
                        if (toClose <= 0)
                        {
                            break;
                        }

                        double lots = order.GetLots();

                        if (lots <= toClose)
                        {
                            if (!CloseOrder(order))
                            {
                                return OrderResult.Error;
                            }
                            toClose -= lots;
                        }
                        else
                        {
                            if (!CloseOrder(order, toClose))
                            {
                                return OrderResult.Error;
                            }
                            toClose = 0;
                        }
                    }

                    
                }
                else if (currentVolume > TargetVolume)
                {
                    // Otwieramy selle
                    toOpen = Math.Round((currentVolume - TargetVolume) / MinLotSize) * MinLotSize;

                    if (OpenOrder(Symbol, TradeOperation.OP_SELL, toOpen, MagicNumber) == -1)
                    {
                        return OrderResult.Error;
                    }
                }
                else
                {
                    return OrderResult.NoChanges;
                }
            }
            else
            {
                // Zamknij wszystko
            }
            

            return OrderResult.Success;
        }


        public bool CloseOrder(IOrderInfo order)
        {
            return CloseOrder(order, order.GetLots());
        }

        public bool CloseOrder(IOrderInfo order, double lots)
        {
            int count = 0;
            while (!account.Strategy.OrderClose(order.GetTicket(), lots, order.GetClosePrice(), 10, Color.DeepPink))
            {
                if (++count > TRIES)
                {
                    // TODO: Logger
                    System.Console.WriteLine(account.Strategy.GetLastError());
                    return false;
                }
                Thread.Sleep(WAIT_TIME);
            }
            return true;
        }

        public int OpenOrder(string symbol, TradeOperation type, double volume, int MagicNumber)
        {
            int count = 0;
            int ticket = -1;
            if (type == TradeOperation.OP_BUY) {
                while (ticket == -1)
                {
                    ticket = account.Strategy.OrderSend(symbol, type, volume, account.Strategy.Marketinfo(symbol, MarketInfo.MODE_ASK), 10, 0, 0, "", MagicNumber, DateTime.MinValue, Color.SandyBrown);
                    if (++count > TRIES)
                    {
                        // TODO: Logger
                        System.Console.WriteLine(account.Strategy.GetLastError());
                        return ticket;
                    }
                }

            } else if (type == TradeOperation.OP_SELL) {
                while (ticket == -1)
                {
                    ticket = account.Strategy.OrderSend(symbol, type, volume, account.Strategy.Marketinfo(symbol, MarketInfo.MODE_BID), 10, 0, 0, "", MagicNumber, DateTime.MinValue, Color.SandyBrown);
                    if (++count > TRIES)
                    {
                        // TODO: Logger
                        System.Console.WriteLine(account.Strategy.GetLastError());
                        return ticket;
                    }
                }
            }
            
            return ticket;
        }






        public enum OrderResult
        {
            TooLowDifference,
            NoChanges,
            Success,
            Error
        }




    }
}
